package com.zon.len.mp.extend.matedata;

import static com.baomidou.mybatisplus.core.toolkit.StringPool.COMMA;
import static java.util.stream.Collectors.joining;

import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.toolkit.Assert;
import com.baomidou.mybatisplus.core.toolkit.LambdaUtils;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.support.LambdaMeta;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Predicate;
import lombok.Getter;
import org.apache.ibatis.reflection.property.PropertyNamer;

/**
 * @author ZonLen since on 2021/12/18 下午5:16
 */
public class TableMetadata {

  @Getter
  private final TableInfo tableInfo;

  private String allSelectColumns;

  private final Map<String, TableFieldInfo> tableColumns = new ConcurrentHashMap<>();

  private String tableAlias;

  public <T, R> String getColumnAndAlias(SFunction<T, R> function) {
    return tableAlias + StringPool.DOT + getColumn(function);
  }

  public <T, R> String getColumn(SFunction<T, R> function) {
    final LambdaMeta lambda = LambdaUtils.extract(function);
    String fieldName = PropertyNamer.methodToProperty(lambda.getImplMethodName());
    return getColumn(fieldName);
  }

  public String getColumn(String fieldName) {
    if (tableInfo.getKeyColumn().equalsIgnoreCase(fieldName)) {
      return fieldName;
    }
    final TableFieldInfo tableFieldInfo = tableColumns.get(fieldName);
    if (null == tableFieldInfo) {
      return null;
    }
    return tableFieldInfo.getColumn();
  }

  public TableMetadata(TableInfo tableInfo) {
    this.tableInfo = tableInfo;
    this.tableAlias = getAlias();
    for (TableFieldInfo tableFieldInfo : tableInfo.getFieldList()) {
      tableColumns.putIfAbsent(tableFieldInfo.getProperty(), tableFieldInfo);
    }
  }

  public String getAlias() {
    if (StringUtils.isBlank(this.tableAlias)) {
      final Class<?> tableClass = tableInfo.getEntityType();
      Assert.notNull(tableClass, "Can't get the current parser class");
      this.tableAlias = StringUtils.underlineToCamel(tableInfo.getTableName());
    }
    return this.tableAlias;
  }


  public String getTableName() {
    return tableInfo.getTableName();
  }

  /**
   * 如果没有条件，获取当前类的所有查询字段
   */
  public final String getSelectColumnsForAlias(Predicate<TableFieldInfo> predicate) {
    if (null == predicate) {
      if (StringUtils.isNotBlank(allSelectColumns)) {
        return allSelectColumns;
      }
    }
    Class<?> tableClass = tableInfo.getEntityType();
    Assert.notNull(tableClass, "Can't get the current parser class");
    String keySqlSelect = tableInfo.getKeySqlSelect();
    String sqlSelect = tableInfo.getFieldList().stream()
        .filter(predicate != null ? predicate : a -> true)
        .filter(TableFieldInfo::isSelect)
        .map(TableFieldInfo::getSqlSelect)
        .map(this::getColumnAndAlias)
        .collect(joining(COMMA));
    // 不为空代表有主键
    if (StringUtils.isNotBlank(keySqlSelect)) {
      sqlSelect += COMMA + getColumnAndAlias(keySqlSelect);
    }
    if (null == predicate) {
      this.allSelectColumns = sqlSelect;
    }
    return sqlSelect;
  }

  /**
   * 获取 增加别名后的字段
   *
   * @param fieldName 字段
   * @return 别名 + 字段
   */
  private String getColumnAndAlias(String fieldName) {
    return tableAlias + StringPool.DOT + fieldName;
  }

}
